#pragma once

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>

#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#include "Util.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

#define PAGE_404 "404.html"

// 状态码
#define OK 200
#define NOT_FOUNT 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500

static std::string CodeToDesc(int code)
{
    std::string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "NOT_FOUNT";
        break;
    default:
        break;
    }
    return desc;
}
static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2Desc = {
        {".html", "text/html"},
        {".css", "test/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"}};

    auto iter = suffix2Desc.find(suffix);
    if (iter != suffix2Desc.end())
    {
        return iter->second;
    }
    return "";
}
class HttpRequest
{
public:
    std::string request_line;                // 请求行
    std::vector<std::string> request_header; // 请求报头
    std::string blank;                       // 空行
    std::string request_body;                // 请求正文

    // 解析完毕之后的结果
    std::string method;
    std::string uri; // 可能分为两部分 path?args
    std::string version;

    std::unordered_map<std::string, std::string> header_kv;
    int content_length;

    std::string path;
    std::string suffix;       // 后缀
    std::string query_string; // uri带的参数

    bool cgi;
    int size = 0;

public:
    HttpRequest() : content_length(0), cgi(false) {}
    ~HttpRequest() {}
};
class HttpResponse
{
public:
    std::string status_line;                  // 状态行
    std::vector<std::string> response_header; // 响应报头
    std::string blank;                        // 空行
    std::string response_body;                // 响应正文

    int status_code;
    int fd;

public:
    HttpResponse() : blank(LINE_END), status_code(OK), fd(-1) {}
    ~HttpResponse() {}
};

class EndPoint
{
private:
    bool RecvHttpRequestLine()
    {
        std::string &line = _httpRequest.request_line;
        // std::cout << "RecvHttpRequestLine line.size()" << line.size() << std::endl;

        if (Util::ReadLine(_sock, line) > 0)
        {
            line.resize(line.size() - 1); // 去掉'\n'
            LOG(INFO, _httpRequest.request_line);
        }
        else
        {
            _stop = true;
        }
        return _stop;
    }
    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (true)
        {
            line.clear();
            if (Util::ReadLine(_sock, line) <= 0)
            {
                _stop = true;
                break;
            }

            if (line == "\n")
            {
                _httpRequest.blank = line;
                break;
            }
            // std::cout << "RecvHttpRequestHeader line.size()" << line.size() << std::endl;
            line.resize(line.size() - 1);
            _httpRequest.request_header.push_back(line);
            // LOG(INFO, line);
        }
        return _stop;
    }
    void ParseHttpRequestLine()
    {
        std::string &line = _httpRequest.request_line;
        std::stringstream ss(line);
        ss >> _httpRequest.method >> _httpRequest.uri >> _httpRequest.version;

        auto &method = _httpRequest.method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
        // LOG(INFO, _httpRequest.method);
        // LOG(INFO, _httpRequest.uri);
        // LOG(INFO, _httpRequest.version);
    }
    void ParseHttpRequestHeader()
    {
        std::string key;
        std::string value;
        for (auto &iter : _httpRequest.request_header)
        {
            if (Util::CutString(iter, key, value, SEP))
            {
                // std::cout << "debug : " << key << std::endl;
                // std::cout << "debug : " << value << std::endl;
                _httpRequest.header_kv[key] = value;
            }
        }
    }
    bool IsNeedRecvHttpRequestBody()
    {
        if (_httpRequest.method == "POST")
        {
            auto iter = _httpRequest.header_kv.find("Content-Length");
            if (iter != _httpRequest.header_kv.end())
            {
                _httpRequest.content_length = atoi(iter->second.c_str());
                // std::cout << "_httpRequest.content_length" << _httpRequest.content_length << std::endl;
                return true;
            }
        }
        return false;
    }

    bool RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_length = _httpRequest.content_length;
            char ch = 0;
            while (content_length)
            {
                ssize_t n = recv(_sock, &ch, 1, 0);
                if (n > 0)
                {
                    _httpRequest.request_body.push_back(ch);
                    content_length--;
                }
                else
                {
                    _stop = true;
                    break;
                }
            }
        }
        return _stop;
    }

    int ProcessCgi()
    {
        int code = OK;
        // std::cout << "debug: Use CGI Model" << std::endl;
        // 父进程数据
        auto &bin = _httpRequest.path;                  // 要让子进程执行的目标程序
        auto &query_string = _httpRequest.query_string; // get 参数
        auto &body_text = _httpRequest.request_body;    // post
        auto &method = _httpRequest.method;
        int content_length = _httpRequest.content_length;
        auto &response_body = _httpResponse.response_body;

        // 子进程导入环境变量
        std::string query_string_env;
        std::string method_env;
        std::string content_length_env;

        int input[2];
        int output[2];
        if (pipe(input) < 0)
        {
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return code;
        }
        if (pipe(output) < 0)
        {
            LOG(ERROR, "pipe output error");
            code = SERVER_ERROR;
            return code;
        }

        pid_t pid = fork();
        if (pid == 0) // 子进程
        {
            close(input[0]);
            close(output[1]);

            method_env = "METHOD=";
            method_env += method;
            // std::cerr << method_env << std::endl;
            putenv((char *)method_env.c_str());
            // std::cerr << "getenv: " << getenv("METHOD") << std::endl;
            if ("GET" == method)
            {
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv((char *)query_string_env.c_str());
            }
            else if ("POST" == method)
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char *)content_length_env.c_str());
            }
            dup2(output[0], 0); // 读 -> 0 -> output[0]
            dup2(input[1], 1);  // 写 -> 1 -> input[1]
            // 程序替换之后丢失input和output数据，在程序替换之前进行重定向
            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if (pid < 0) // 错误
        {
            LOG(ERROR, "fork error");
            return SERVER_ERROR;
        }
        else // 父进程
        {
            close(input[1]);
            close(output[0]);

            if ("POST" == method)
            {
                const char *start = body_text.c_str();
                int total = 0;                                                                                             // 一共写的
                int size = 0;                                                                                              // 一次写的
                while ((total < content_length) && (size = write(output[1], start + total, body_text.size() - total)) > 0) // 一次可能写不完
                {
                    total += size;
                }
            }

            char ch = 0;
            while (read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            }
            // std::cout << "debug: ProcessCgi: " << response_body << std::endl;
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if (ret == pid)
            {
                if (WIFEXITED(status)) // 正常退出
                {
                    if (WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else
                    {
                        code = BAD_REQUEST;
                    }
                }
                else
                {
                    code = SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
        }
        return code;
    }

    int ProcessNonCgi() // 返回静态网页
    {
        _httpResponse.fd = open(_httpRequest.path.c_str(), O_RDONLY);
        if (_httpResponse.fd >= 0)
        {
            return OK;
        }
        return NOT_FOUNT;
    }
    void BuildOkResponse()
    {
        std::string line = "Content-Type: ";
        line += Suffix2Desc(_httpRequest.suffix);
        line += LINE_END;
        _httpResponse.response_header.push_back(line);

        line = "Content-Lenth: ";
        if (_httpRequest.cgi)
        {
            line += std::to_string(_httpResponse.response_body.size());
        }
        else
        {
            line += std::to_string(_httpRequest.size);
        }
        line += LINE_END;
        _httpResponse.response_header.push_back(line);
    }
    void HandlerError(std::string page)
    {
        _httpRequest.cgi = false;
        // 返回404页面
        _httpResponse.fd = open(page.c_str(), O_RDONLY);
        if (_httpResponse.fd > 0)
        {
            struct stat st;
            stat(page.c_str(), &st);
            _httpRequest.size = st.st_size;

            std::string line = "Content-Type: text/html";
            line += LINE_END;
            _httpResponse.response_header.push_back(line);

            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            _httpResponse.response_header.push_back(line);
        }
    }
    void BuildHttpResponseHelper()
    {
        auto &code = _httpResponse.status_code;
        // 构建状态行
        auto &status_line = _httpResponse.status_line;
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += CodeToDesc(code);
        status_line += LINE_END;

        // 构建响应正文，响应报头
        std::string path = WEB_ROOT;
        path += "/";
        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUNT:
            path += PAGE_404;
            HandlerError(path);
            break;
        case BAD_REQUEST:
            path += PAGE_404;
            HandlerError(path);
            break;
        case SERVER_ERROR:
            path += PAGE_404;
            HandlerError(path);
            break;
        default:
            break;
        }
    }

public:
    EndPoint(int sock) : _sock(sock)
    {
        // std::cout << _sock << std::endl;
    }
    void RecvHttpRequest()
    {
        // RecvHttpRequestLine();    // 读取请求行
        // RecvHttpRequestHeader();  // 读取报头
        // ParseHttpRequestLine();   // 解析请求行
        // ParseHttpRequestHeader(); // 解析报头
        // RecvHttpRequestBody();    // 读取正文

        if (RecvHttpRequestLine() != true && RecvHttpRequestHeader() != true)
        {
            ParseHttpRequestLine();
            ParseHttpRequestHeader();
            RecvHttpRequestBody();
        }
    }

    void BuildHttpResponse()
    {
        std::string _path;
        int &code = _httpResponse.status_code;
        struct stat st;
        int found = 0;
        if (_httpRequest.method != "GET" && _httpRequest.method != "POST")
        {
            code = BAD_REQUEST;
            LOG(WARING, "method is not right");
            goto END;
        }
        if (_httpRequest.method == "GET") // get方法可能在uri带参数
        {
            size_t pos = _httpRequest.uri.find('?');
            if (pos != std::string::npos) // 带参数
            {
                Util::CutString(_httpRequest.uri, _httpRequest.path, _httpRequest.query_string, "?");
                _httpRequest.cgi = true;
                // std::cout << "debug: " << _httpRequest.path << std::endl;
                // std::cout << "debug: " << _httpRequest.query_string << std::endl;
            }
            else
            {
                _httpRequest.path = _httpRequest.uri;
            }
        }
        else if (_httpRequest.method == "POST")
        {
            _httpRequest.cgi = true;
            _httpRequest.path = _httpRequest.uri;
        }

        _path = _httpRequest.path;
        _httpRequest.path = WEB_ROOT;
        _httpRequest.path += _path;

        if (_httpRequest.path[_httpRequest.path.size() - 1] == '/')
        {
            _httpRequest.path += HOME_PAGE;
        }
        // std::cout << "debug: " << _httpRequest.path << std::endl;

        if (stat(_httpRequest.path.c_str(), &st) == 0)
        {
            // 说明资源存在
            if (S_ISDIR(st.st_mode))
            {
                // 说明path是一个目录
                _httpRequest.path += "/"; // 虽然是目录，但不会以'/'结尾
                _httpRequest.path += HOME_PAGE;
                // path更新，重新获取一下stat
                stat(_httpRequest.path.c_str(), &st);
            }
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 可执行程序，特殊处理
                _httpRequest.cgi = true;
            }
            _httpRequest.size = st.st_size;
        }
        else
        {
            // 说明资源不存在
            code = NOT_FOUNT;
            LOG(WARING, _httpRequest.path + " NOT_FOUNT!");
            goto END;
        }
        // path后缀
        found = _httpRequest.path.rfind(".");
        if (found == std::string::npos)
        {
            _httpRequest.suffix = ".html";
        }
        else
        {
            _httpRequest.suffix = _httpRequest.path.substr(found);
        }

        if (_httpRequest.cgi)
        {
            code = ProcessCgi();
        }
        else
        {
            code = ProcessNonCgi(); // 返回静态网页
        }
    END:

        BuildHttpResponseHelper();

        return;
    }
    void SendHttpResponse()
    {
        send(_sock, _httpResponse.status_line.c_str(), _httpResponse.status_line.size(), 0);
        for (auto &iter : _httpResponse.response_header)
        {
            send(_sock, iter.c_str(), iter.size(), 0);
        }
        send(_sock, _httpResponse.blank.c_str(), _httpResponse.blank.size(), 0);

        if (_httpRequest.cgi)
        {
            auto &response_body = _httpResponse.response_body;
            const char *start = response_body.c_str();
            ssize_t size = 0;
            ssize_t total = 0;

            std::cout << "cgi send response_body = " << _httpResponse.response_body << std::endl;
            while ((total < response_body.size()) && (size = send(_sock, start + total, response_body.size() - total, 0)) > 0)
            {
                // std::cout << "cgi send size = " << size << std::endl;
                // printf("cgi send size = %u\n", size);
                // total += size;
                total = (total + size);

                // std::cout << "cgi send total = " << size << std::endl;
                // std::cout << "cgi send total + size = " << (total = (total + size)) << std::endl;
                // std::cout << "cgi send *start = " << *start << std::endl;
            }
        }
        else
        {
            sendfile(_sock, _httpResponse.fd, nullptr, _httpRequest.size);
            close(_httpResponse.fd);
        }
    }

    ~EndPoint()
    {
        close(_sock);
        // std::cout << "close sock : " << _sock << std::endl;
    }

    bool IsStop()
    {
        return _stop;
    }

private:
    int _sock;
    HttpRequest _httpRequest;
    HttpResponse _httpResponse;
    bool _stop = false;
};

class CallBack
{
public:
    CallBack() {}

    void operator()(int sock)
    {
        HandlerRequest(sock);
    }
    void HandlerRequest(int sock)
    {
        LOG(INFO, "HandlerRequest Begin");

        // std::cout << "get a new link...: " << sock << std::endl;

        // #ifndef DEGUG
        // #define DEGUG
        //         char buffer[4096];
        //         ssize_t n = recv(sock, buffer, sizeof(buffer), 0);
        //         buffer[n] = 0;
        //         std::cout << buffer << std::endl;
        // #endif
        // std::string line;
        // Util::ReadLine(sock, line);
        // std::cout << line << std::endl;

        // close(sock);
        // std::cout << "close sock: " << sock << std::endl;

        EndPoint *ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        if (ep->IsStop() != true)
        {
            LOG(INFO, "没有读取出错，开始构建、发送");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else
        {
            LOG(WARING, "读取出错，停止构建、发送");
        }
        delete ep;
        LOG(INFO, "HandlerRequest End");
    }

    ~CallBack() {}
};
